import * as step from '@flow-step/step-toolkit'
import * as fs from 'fs'
import {installKubectlSae} from './install'
import {hasFilesInDirectory} from './util'
import {IParams} from './params'
import path from 'path'
import {chmodSync} from 'fs'
import {setEnv} from './env'
import {polyglot} from './i18n'

const SEP = path.sep

export async function execute(params: IParams): Promise<void> {
    // install kubectl-sae
    try {
        await installKubectlSae()
    } catch (err) {
        throw new Error(`failed to install kubectl-sae, err: ${err}`)
    }

    // set env
    await setEnv()

    const kubectlSae = `kubectl-sae`

    step.infoV1('[USER] NAMESPACE=' + params.namespace)
    step.infoV1('[USER] PATH_TO_YAML=' + params.pathToYaml)

    let kubectlSaeArgs = []
    
    // set namespace arg
    if (params.namespace !== '') {
        kubectlSaeArgs.push(`--namespace=${params.namespace}`)
    }

    // check kubectl-sae version
    step.infoV1("")
    step.infoV1(polyglot.t("kubectl_sae_basic_information"))
    step.infoV1("")
    step.debug(`exec command: ${kubectlSae} version`)
    let resultCode = await step.exec.call(kubectlSae, ['version'], {
        listener: {
            stdoutLine: (line: string) => step.infoV1(line),
            stderrLine: (line: string) => step.infoV1(line)
        }
    })
    if (resultCode !== 0) {
        step.warnV1(`${kubectlSae} version execute failed with exit code: ${resultCode}`)
    }

    step.infoV1(polyglot.t("apply_manifests_definition_file"))
    const manifestFilePath = `${params.projectDir}/.manifest`
    fs.mkdirSync(manifestFilePath, {recursive: true})

    step.infoV1("")
    step.infoV1(polyglot.t("create_cache_directory_for_publishing_resources"))
    step.infoV1(`${params.images}`)


    // copy outputs2template binary to workspace
    const outputs2templateSourceBin = __dirname + SEP + 'outputs2template'
    const outputs2templateBin = path.join(params.workSpace, 'outputs2template')
    step.debug(`copy outputs2template binary: ${outputs2templateSourceBin} to ${outputs2templateBin}`)

    if (fs.existsSync(outputs2templateBin)) {
        step.debug(`remove outputs2templateBin: ${outputs2templateBin}`)
        fs.unlinkSync(outputs2templateBin);
    }
    fs.copyFileSync(outputs2templateSourceBin, outputs2templateBin)

    try {
        const stats = fs.statSync(outputs2templateBin)
        if ((stats.mode & 0o777) === 0o755) {
            step.debug(`Permissions for ${outputs2templateBin} is already 755`)
        } else {
            chmodSync(outputs2templateBin, 0o755)
            step.debug(`Permissions for ${outputs2templateBin} set to 755`)
        }
    } catch (err) {
        // @ts-ignore
        throw new Error(`Error setting file permissions: ${err.message}`)
    }

    // replace variables in yaml
    const outputs2templateBinArgs = [`-data=${params.images}`, `-searchDir=${path.join(params.projectDir, params.pathToYaml)}`, `-tmpDir=${manifestFilePath}`]
    step.debug(`exec command: ${outputs2templateBin} ${outputs2templateBinArgs.join(' ')}`)
    resultCode = await step.exec.call(outputs2templateBin, outputs2templateBinArgs, {
        listener: {
            stdoutLine: (line: string) => step.infoV1(line),
            stderrLine: (line: string) => step.warnV1(line)
        }
    })
    if (resultCode !== 0) {
        throw new Error(`outputs2template exec failed, exit code: ${resultCode}`)
    }

    step.infoV1(polyglot.t("yaml_template_processed_successfully", {pathToYaml: params.pathToYaml}))

    step.infoV1(polyglot.t("all_deployment_resources"))
    step.debug(`exec command: ls -lR ${manifestFilePath}`)
    resultCode = await step.exec.call('ls', ['-lR', manifestFilePath], {
        listener: {
            stdoutLine: (line: string) => step.infoV1(line),
            stderrLine: (line: string) => step.warnV1(line)
        }
    })
    if (resultCode !== 0) {
        throw new Error(`ls -lR ${manifestFilePath} failed, exit code: ${resultCode}`)
    }

    const hasFile = await hasFilesInDirectory(manifestFilePath)
    if (!hasFile) {
        throw new Error(polyglot.t("no_resources_found"))
    }

    step.infoV1(polyglot.t("list_of_resources_to_be_released"))

    // set dry-run=client
    let dryRun = '--dry-run=client'

    // record resources
    let resourceNames: string[] = []
    step.infoV1(`exec command: ${kubectlSae} ${kubectlSaeArgs.concat(['apply', '-f', manifestFilePath, dryRun]).join(' ')}`)
    resultCode = await step.exec.call(kubectlSae, kubectlSaeArgs.concat(['apply', '-f', manifestFilePath, dryRun]), {
        listener: {
            stdoutLine: (line: string) => {
                const resourceName = line.split(' ')[0]
                if (resourceName !== '') {
                    step.infoV1(resourceName)
                    resourceNames.push(resourceName)
                }
            },
            stderrLine: (line: string) => step.warnV1(line)
        }
    })
    if (resultCode !== 0) {
        throw new Error(`${kubectlSae} apply -f ${manifestFilePath} ${dryRun} failed, exit code: ${resultCode}`)
    }

    step.debug(`resourceNames: ${resourceNames}`)

    let subCommand = 'apply'

    step.infoV1("")
    step.infoV1(`${polyglot.t("deploy_changes_to_sae")} kubectl ${subCommand} -f ${manifestFilePath}`)
    step.debug(`exec command: ${kubectlSae} ${kubectlSaeArgs.concat([subCommand, '-f', manifestFilePath]).join(' ')}`)
    resultCode = await step.exec.call(kubectlSae, kubectlSaeArgs.concat([subCommand, '-f', manifestFilePath]), {
        listener: {
            stdoutLine: (line: string) => step.infoV1(line),
            stderrLine: (line: string) => step.warnV1(line)
        }
    })
    if (resultCode !== 0) {
        throw new Error(`Command exec, exit code: ${resultCode}`)
    }

    if (params.namespace !== '') {
        step.infoV1("")
        step.infoV1(polyglot.t("print_the_current_task_execution_result"))

        for (const resource of resourceNames) {
            step.infoV1("")
            step.infoV1(`${resource}`)
            step.infoV1("")

            step.debug(`exec command: ${kubectlSae} ${kubectlSaeArgs.concat(['get', resource]).join(' ')}`)
            await step.exec.call(kubectlSae, kubectlSaeArgs.concat(['get', resource]), {
                listener: {
                    stdoutLine: (line: string) => step.infoV1(line),
                    stderrLine: (line: string) => step.warnV1(line)
                }
            })

            step.infoV1(polyglot.t("yaml_of_sae_online_resources"))

            step.debug(`exec command: ${kubectlSae} ${kubectlSaeArgs.concat(['get', resource, '-o', 'yaml']).join(' ')}`)
            await step.exec.call(kubectlSae, kubectlSaeArgs.concat(['get', resource, '-o', 'yaml']), {
                listener: {
                    stdoutLine: (line: string) => step.infoV1(line),
                    stderrLine: (line: string) => step.warnV1(line)
                }
            })

        }
    }

    step.addOutput('RERUNABLE=true')

    step.infoV1(polyglot.t("clean_up_the_cache_directory"))
    fs.rmSync(manifestFilePath, {recursive: true})
    // rm copy outputs2templateBin
    fs.unlinkSync(outputs2templateBin);
}